<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <h1>工厂设计模式分为：简单工厂模式、工厂方法模式、抽象工厂模式</h1>

    <script>
      /*
       * 1.简单工厂模式:是工厂函数返回实例化对象或者对象，工厂函数作为一个方法。
       *   简单工厂模式的理念：就是创建对象；
       *   当子类有相同的地方可以抽象提取出来共用，可以简单创建一个对象，然后通过这个对象大量拓展方法和属性，并在最终将对象返回出来。
       */

      function creatBook(name, time, type) {
        var obj = new Object();
        obj.name = name;
        obj.time = time;
        obj.type = type;
        obj.getName = function() {
          console.log(this.name);
        };

        switch (type) {
          case "js":
            obj.descript = "这是一本js书";
            obj.getName = function() {
              console.log(this.name + "才是世界最好的语言");
            };
            break;
          case "php":
            obj.descript = "这是php语言";
            break;
          case "java":
            obj.descript = "这是java语言";
            break;
        }
        return obj;
      }

      var book = new creatBook("javaScript", "2020-3-2", "js");
      console.log(book);
      console.log(book.name);

      /**
       * 2.工厂方法模式：将实际创建对象工作推迟到子类当中，即将创建对象的基类放在工厂方法类的原型中，这样核心类就成了抽象类。
       */
      var Factory = function(type, content) {
        if (this instanceof Factory) {
          var add = this[type][content];
          return add;
        } else {
          return new Factory(type, content);
        }
      };

      Factory.prototype = {
        js: function(content) {
          console.log(content);
        },
        java: function(content) {
          console.log(content);
        },
        python: function(content) {
          console.log(content);
        }
      };

      var datas = [
        { type: "js", content: "这是js语言" },
        { type: "java", content: "这是java语言" },
        { type: "python", content: "这是python语言" }
      ];

      datas.forEach(function(item, index) {
        new Factory(item.type, item.content);
      });

      /**
       * 3. 抽象工厂模式:实现子类继承父类的方法
       * 抽象工厂模式，通过对类的工厂抽象，使其业务用于对产品类簇的创建，而不负责某一具体类产品的实例。
       * 抽象类：是一种声明但不能使用的类，当使用时会抛出错误；在js中需要手动抛出错误提示。
       * 因为抽象工厂是个方法不需要实例化对象，故只需要一份，因此直接为抽象工厂添加类的属性即可。
       * 实例地址：https://www.cnblogs.com/netUserAdd/p/10404150.html
       */
      let agency = function(subType, superType) {
        //判断抽象工厂中是否含有该抽象类
        if (typeof agency[superType] === "function") {
          var F = function() {};
          //继承父类属性和方法
          F.prototype = new agency[superType]();
          //将子类的constructor指向子类
          subType.prototype = subType;
          //子类原型继承父类
          subType.prototype = new F();
        } else {
          throw new Error("抽象类不存在");
        }
      };

      //鼠标抽象类
      agency.mouseShop = function() {
        this.type = "鼠标";
      };

      agency.mouseShop.prototype = {
        getName: function() {
          return new Error("抽象方法不能调用");
        }
      };

      //键盘类
      agency.keyboardShop = function() {
        this.type = "键盘";
      };
      agency.keyboardShop.prototype = {
        getName: function() {
          return new Error("抽象方法不能调用");
        }
      };

      //普通鼠标子类
      function mouse(name) {
        this.name = name;
        this.item = ["买我，我线长", "玩游戏贼溜"];
      }
      //抽象工厂实现鼠标类的继承
      agency(mouse, "mouseShop");
      //子类中重写抽象方法
      mouse.prototype.getName = function() {
        return this.name;
      };

      //普通键盘子类
      function Keyboard(name) {
        this.name = name;
        this.item = ["行，你买它吧", "没键盘看你咋玩"];
      }
      //抽象工厂实现键盘类的继承
      agency(Keyboard, "KeyboardShop");
      //子类中重写抽象方法
      Keyboard.prototype.getName = function() {
        return this.name;
      };

      //实例化鼠标
      let mouseA = new mouse("联想");
      console.log(mouseA.getName(), mouseA.type); //联想 鼠标
      let mouseB = new mouse("戴尔");
      console.log(mouseB.getName(), mouseB.type); //戴尔 鼠标

      //实例化键盘
      let KeyboardA = new Keyboard("联想");
      console.log(KeyboardA.getName(), KeyboardA.type); //联想 键盘
      let KeyboardB = new Keyboard("戴尔");
      console.log(KeyboardB.getName(), KeyboardB.type); //戴尔 键盘
    </script>
  </body>
</html>
